package dawnland03.model.action;

import dawnland03.model.action.handler.ActionHandler;
import dawnland03.model.action.parameter.ActionInputParam;
import dawnland03.model.action.parameter.ActionOutputParam;
import dawnland03.model.action.parameter.OutputParameters;
import dawnland03.model.entity.Entity;
import dawnland03.model.entity.attribute.PhysicalAttribute;
import dawnland03.model.task.BaseTask;
import org.apache.log4j.Logger;

import java.util.HashMap;
import java.util.Map;

/**
 * User: Petru Obreja (obrejap@yahoo.com)
 * Date: Jan 18, 2010
 * Time: 10:35:53 PM
 */
public abstract class ActionImpl implements Action {
    protected final Entity owner;
    protected final ActionType actionType;
    protected final Map<ActionInputParam, Object> actionInputParams;
    protected final OutputParameters outputParameters;
    protected final Map<PhysicalAttribute, Integer> physicalAttributeRequirements;
    protected final Map<PhysicalAttribute, Double> physicalAttributeMultipliers;
    protected final Long duration;
    protected final ActionHandler actionHandler;

    private BaseTask parentBaseTask; //if null, then the Action is not inside a task and works as that

    private static Logger logger = Logger.getLogger(ActionImpl.class.getName());

    protected ActionImpl(Entity owner, ActionType actionType, Map<ActionInputParam, Object> actionInputParams,
                         Map<PhysicalAttribute, Integer> physicalAttributeRequirements,
                         Map<PhysicalAttribute, Double> physicalAttributeMultiplier,
                         long duration, ActionHandler actionHandler) {
        this.owner = owner;
        this.actionType = actionType;
        if (actionInputParams != null) {
            this.actionInputParams = actionInputParams;
        } else {
            this.actionInputParams = new HashMap<ActionInputParam, Object>();
        }
        outputParameters = new OutputParameters();
        outputParameters.addOutputParamValue(ActionOutputParam.DURATION, duration);
        if (physicalAttributeRequirements != null) {
            this.physicalAttributeRequirements = physicalAttributeRequirements;
        } else {
            this.physicalAttributeRequirements = new HashMap<PhysicalAttribute, Integer>();
        }
        if (physicalAttributeMultiplier != null) {
            this.physicalAttributeMultipliers = physicalAttributeMultiplier;
        } else {
            this.physicalAttributeMultipliers = new HashMap<PhysicalAttribute, Double>();
        }
        /*validations:
            physicalAttributeRequirements.keySet must be equal with physicalAttributeMultipliers.keySet;
            all physicalAttributeRequirements must be greater than zero;
            all physicalAttributeMultipliers must be greater than zero;
        */
        if (this.physicalAttributeRequirements.keySet().size() != this.physicalAttributeMultipliers.keySet().size()) {
            throw new IllegalStateException("physicalAttributeRequirements.keySet must be equal with physicalAttributeMultipliers.keySet");
        }
        for (PhysicalAttribute physicalAttribute : this.physicalAttributeRequirements.keySet()) {
            if (!this.physicalAttributeMultipliers.keySet().contains(physicalAttribute)) {
                throw new IllegalStateException("physicalAttributeRequirements.keySet must be equal with physicalAttributeMultipliers.keySet");
            }
            if (this.physicalAttributeRequirements.get(physicalAttribute) <= 0) {
                throw new IllegalStateException("physicalAttributeRequirement " + physicalAttributeRequirements + " must be greater than zero");
            }
            if (this.physicalAttributeMultipliers.get(physicalAttribute) <= 0) {
                throw new IllegalStateException("physicalAttributeMultiplier " + physicalAttributeRequirements + " must be greater than zero");
            }
        }
        this.duration = duration;
        this.actionHandler = actionHandler;
    }

    public void act() {
        if (logger.isDebugEnabled()) {
            logger.debug(this + " starts.");
        }
        ActionRunnable actionRunnable = new ActionRunnable(this);
        if (duration > 0) {
            ActionExecutor.scheduleAction(actionRunnable, duration);
        } else {
            actionRunnable.run(); //it will execute the run method without waiting; no thread is created
        }
    }

    protected void endAction() {
        outputParameters.addOutputParamValue(ActionOutputParam.OWNER_SNAPSHOT, owner.getEntitySnapshot());
        //handle the action
        actionHandler.handle(this);
        if (parentBaseTask != null) {
            parentBaseTask.signalActionEnd();
        }
    }

    public Entity getOwner() {
        return owner;
    }

    public ActionType getActionType() {
        return actionType;
    }

    public void setParentBaseTask(BaseTask parentBaseTask) {
        this.parentBaseTask = parentBaseTask;
    }

    public Object getInputParam(ActionInputParam actionInputParam) {
        return actionInputParams.get(actionInputParam);
    }

    public OutputParameters getOutputParameters() {
        return outputParameters;
    }

    public Map<PhysicalAttribute, Integer> getPhysicalAttributeRequirements() {
        return physicalAttributeRequirements;
    }

    public Map<PhysicalAttribute, Double> getPhysicalAttributeMultipliers() {
        return physicalAttributeMultipliers;
    }

    public Long getDuration() {
        return duration;
    }

    public String toString() {
        return "A[" + owner + "-" + actionType + "]";
    }
}
